﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using Common;
using Common.CustomException;
using Common.ParameterLocator;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;

namespace SpecFlowTfsConnector
{
    public class TfsConnector
    {
        private readonly TfsTeamProjectCollection _tfsProjects;

        public TfsConnector()
        {
            var serverUrl = ServiceParameter.Instance.Parameter.TfsServerUrl;

            if (string.IsNullOrEmpty(serverUrl))
                throw new TfsException(Error.TfsServerUrlIsNotProvided);

            var serverUri = new Uri(serverUrl);

            try
            {
                if (TfsCredential.CredentialsAreProvided())
                {
                    _tfsProjects = new TfsTeamProjectCollection(serverUri,
                                                                TfsCredential.CreateCredentialsFromParameters());
                }
                else
                {
                    _tfsProjects = new TfsTeamProjectCollection(serverUri);
                }

                _tfsProjects.EnsureAuthenticated();
            }
            catch (WebException ex)
            {
                throw new TfsException(Error.BadTfsServerUrl, ex);
            }
            catch (Exception ex)
            {
                throw new TfsException(Error.FailToConnectToTfsServer, ex);
            }
        }

        public WorkItemCollection GetTfsUserStoriesWorkItem(string projectName)
        {
            var project = GetProjectByName(projectName);

            var tfsQuery = GetUserStoryQuery(projectName);

            if (!TfsProjectContainsWorkItemType(project))
                throw new TfsException(Error.ProjectDoesNotContainThisWorkItemType);

            try
            {
                return project.Store.Query(tfsQuery);
            }
            catch (ValidationException validationException)
            {
                throw new TfsException(string.Format(Resource.BAD_TFS_QUERY, tfsQuery), validationException);
            }
            catch (Exception exception)
            {
                throw new TfsException(string.Format(Resource.QUERY_EXECUTION_ERROR, tfsQuery), exception);
            }
        }

        private Project GetProjectByName(string projectName)
        {
            if (IsExistingProject(projectName))
            {
                return GetProjects()[projectName];
            }
            throw new TfsException(string.Format(Resource.PROJECT_NOT_DECLARED_IN_TFS, projectName));
        }

        public ProjectCollection GetProjects()
        {
            return _tfsProjects.GetService<WorkItemStore>().Projects;
        }

        private bool IsExistingProject(string projectName)
        {
            return GetProjects().Contains(projectName);
        }

        private static bool TfsProjectContainsWorkItemType(Project project)
        {
            return project.WorkItemTypes.Contains(ServiceParameter.Instance.Parameter.TfsWorkitemType);
        }

        private static string GetUserStoryQuery(string projectName)
        {
            if (string.IsNullOrEmpty(ServiceParameter.Instance.Parameter.TfsWorkitemType))
                throw new VisualStudioParameterException(Error.WorkItemTypeIsNotProvided);

            if (string.IsNullOrEmpty(ServiceParameter.Instance.Parameter.TfsQueryUserStory))
                throw new VisualStudioParameterException((Error.TfsQueryIsNotProvided));

            return string.Format(ServiceParameter.Instance.Parameter.TfsQueryUserStory,
                                 ServiceParameter.Instance.Parameter.TfsWorkitemType, projectName);
        }

        public static List<WorkItem> GetTestsCaseWorkItemByUserStory(WorkItem userStoryWorkItem)
        {
            if (userStoryWorkItem == null)
                throw new ArgumentNullException(Resource.TFS_ITEM_CANNOT_BE_NULL);

            return
                (from WorkItemLink itemLink in userStoryWorkItem.WorkItemLinks
                 select userStoryWorkItem.Store.GetWorkItem(itemLink.TargetId)).
                    ToList();
        }
    }
}